using System;
using System.Collections.Generic;
using System.IO;
using GameFramework.Extensions;
using GameFramework.GameObjects;

namespace GameFramework.Managers
{
    /// <summary>
    /// The screen manager.
    /// </summary>
    public class ScreenManager : GameObject
    {
        /// <summary>
        /// The stack of screens.
        /// </summary>
        public readonly Stack<Screen> Screens = new Stack<Screen>();

        /// <summary>
        /// Navigate toward a screen.
        /// </summary>
        /// <param name="screen">The new screen to show.</param>
        public void NavigateTo(Screen screen)
        {
            OnClosingCurrentScreen();
            Screens.Push(screen);
            OnActivateCurrentScreen();
        }

        /// <summary>
        /// Navigate to the previous screen.
        /// </summary>
        public void NavigateBack()
        {
            if (Screens.Count > 0)
            {
                OnClosingCurrentScreen();
                Screens.Pop();
                OnActivateCurrentScreen();
            }
        }

        /// <summary>
        /// Update the current screen.
        /// </summary>
        public override void Update()
        {
            base.Update();

            if (Screens.Count > 0)
            {
                Screens.Peek().Update();
            }
        }

        /// <summary>
        /// Draw the current screen.
        /// </summary>
        public override void Draw()
        {
            base.Draw();

            if (Screens.Count > 0)
            {
                Screens.Peek().Draw();
            }
        }

        /// <summary>
        /// Deserialize the state from the given stream.
        /// </summary>
        /// <param name="reader">The reader.</param>
        public override void Read(BinaryReader reader)
        {
            base.Read(reader);

            int count = reader.ReadInt();

            for (int i = 0; i < count; ++i)
            {
                string typeName = reader.ReadString();

                if (string.IsNullOrEmpty(typeName))
                {
                    continue;
                }

                Type screenType = Type.GetType(typeName);

                if (screenType == null)
                {
                    continue;
                }

                var screen = Activator.CreateInstance(screenType) as Screen;

                if (screen == null)
                {
                    continue;
                }

                screen.Read(reader);

                Screens.Push(screen);
            }

            OnActivateCurrentScreen();
        }

        /// <summary>
        /// Serialize the state into the given stream.
        /// </summary>
        /// <param name="writer">The writer.</param>
        public override void Write(BinaryWriter writer)
        {
            base.Write(writer);

            writer.WriteInt(Screens.Count);

            var screens = Screens.ToArray();

            for (int i = 0; i < screens.Length; ++i)
            {
                var screen = screens[screens.Length - i - 1];

                writer.WriteString(screen.GetType().AssemblyQualifiedName);
                screen.Write(writer);
            }
        }

        /// <summary>
        /// Call the screen activate method on the current screen.
        /// </summary>
        private void OnActivateCurrentScreen()
        {
            if (Screens.Count > 0)
            {
                Screens.Peek().ScreenActivated();
            }
        }

        /// <summary>
        /// Call the screen closing method on the current screen.
        /// </summary>
        private void OnClosingCurrentScreen()
        {
            if (Screens.Count > 0)
            {
                Screens.Peek().ScreenClosing();
            }
        }
    }
}